﻿// <copyright file="BoundingBox.cs" company="WorldWind, NASA">
// Copyright (c) 2009-2010 All Right Reserved
// </copyright>
// <author>nasa</author>
// <email>dentoby@gmail.com</email>
// <date>2010-4</date>
// <summary>This is part of Image.NET project.</summary>

namespace SPLWHU.Core.Vectors
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// The summary of bounding box.
    /// </summary>
    public class BoundingBox
    {
        /// <summary>
        /// The left bottom point.
        /// </summary>
        private Point2d min;

        /// <summary>
        /// The right top point.
        /// </summary>
        private Point2d max;

        /// <summary>
        /// Initializes a new instance of the <see cref="BoundingBox"/> class.
        /// </summary>
        /// <param name="minX">The min X.</param>
        /// <param name="minY">The min Y.</param>
        /// <param name="maxX">The max X.</param>
        /// <param name="maxY">The max Y.</param>
        public BoundingBox(double minX, double minY, double maxX, double maxY)
        {
            if (minX > maxX)
            {
                double swap = minX;
                minX = maxX;
                maxX = swap;
            }

            if (minY > maxY)
            {
                double swap = minY;
                minY = maxY;
                maxY = swap;
            }

            this.min = new Point2d(minX, minY);
            this.max = new Point2d(maxX, maxY);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BoundingBox"/> class.
        /// </summary>
        /// <param name="p1">The point p1.</param>
        /// <param name="p2">The point p2.</param>
        public BoundingBox(Point2d p1, Point2d p2)
            : this(p1.X, p1.Y, p2.X, p2.Y)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BoundingBox"/> class.
        /// </summary>
        /// <param name="box">The box to be copyed.</param>
        public BoundingBox(BoundingBox box)
        {
            this.min = new Point2d(box.Left, box.Bottom);
            this.max = new Point2d(box.Right, box.Top);
        }

        /// <summary>
        /// Gets the left side.
        /// </summary>
        /// <value>The left side.</value>
        public double Left
        {
            get { return this.min.X; }
        }

        /// <summary>
        /// Gets the bottom side.
        /// </summary>
        /// <value>The bottom side.</value>
        public double Bottom
        {
            get { return this.min.Y; }
        }

        /// <summary>
        /// Gets the right side.
        /// </summary>
        /// <value>The right.</value>
        public double Right
        {
            get { return this.max.X; }
        }

        /// <summary>
        /// Gets the top side.
        /// </summary>
        /// <value>The top side.</value>
        public double Top
        {
            get { return this.max.Y; }
        }

        /// <summary>
        /// Gets the width.
        /// </summary>
        /// <value>The width.</value>
        public double Width
        {
            get { return this.max.X - this.min.X; }
        }

        /// <summary>
        /// Gets the height.
        /// </summary>
        /// <value>The height.</value>
        public double Height
        {
            get { return this.max.Y - this.min.Y; }
        }

        /// <summary>
        /// Gets the centroid.
        /// </summary>
        /// <value>The centroid.</value>
        public Point2d Centroid
        {
            get { return (this.min + this.max) / 2; }
        }

        /// <summary>
        /// Gets the long axis (0, the x-axis; 1, the y-axis).
        /// </summary>
        /// <value>The long axis.</value>
        public int LongAxis
        {
            get
            {
                if (this.Width > this.Height)
                {
                    return 0;
                }
                else
                {
                    return 1;
                }
            }
        }

        /// <summary>
        /// Intersectses the specified box.
        /// </summary>
        /// <param name="box">The testing box.</param>
        /// <returns><c>True</c>, if the two bounding box inserts with each other; otherwise, <c>false</c></returns>
        public bool Intersects(BoundingBox box)
        {
            if (box != null)
            {
                return !(box.Left > this.Right ||
                         box.Right < this.Left ||
                         box.Bottom > this.Top ||
                         box.Top < this.Bottom);
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Determines whether [contains] [the specified box].
        /// </summary>
        /// <param name="box">The testing box.</param>
        /// <returns>
        /// <c>true</c> if [contains] [the specified box]; otherwise, <c>false</c>.
        /// </returns>
        public bool Contains(BoundingBox box)
        {
            return (this.Left < box.Left) && (this.Top > box.Top) && (this.Bottom < box.Bottom) && (this.Right > box.Right);
        }

        /// <summary>
        /// Dispartses the specified box.
        /// </summary>
        /// <param name="box">The testing box.</param>
        /// <returns><c>true</c> if this box disparts [the specified box]; otherwise, <c>false</c>.</returns>
        public bool Disparts(BoundingBox box)
        {
            return (this.Left > box.Right) || (this.Right < box.Left) || (this.Top < box.Bottom) || (this.Bottom > box.Top);
        }

        /// <summary>
        /// Gets the intersection of two bouding boxes.
        /// </summary>
        /// <param name="box">Another bounding box.</param>
        /// <returns>The intersection of two bounding boxes.</returns>
        public BoundingBox Intersection(BoundingBox box)
        {
            if (box == null)
            {
                return null;
            }
            else
            {
                return new BoundingBox(
                    Math.Max(this.Left, box.Left),
                    Math.Max(this.Bottom, box.Bottom),
                    Math.Min(this.Right, box.Right),
                    Math.Min(this.Top, box.Top));
            }
        }

        /// <summary>
        /// Joins the two bouding boxes.
        /// </summary>
        /// <param name="box">Another box.</param>
        /// <returns>The result of join of two bouding boxes.</returns>
        public BoundingBox Joint(BoundingBox box)
        {
            if (box == null)
            {
                return null;
            }
            else
            {
                return new BoundingBox(
                    Math.Min(this.Left, box.Left),
                    Math.Min(this.Bottom, box.Bottom),
                    Math.Max(this.Right, box.Right),
                    Math.Max(this.Top, box.Top));
            }
        }

        /// <summary>
        /// Clones this instance.
        /// </summary>
        /// <returns>The new copy of this bounding box.</returns>
        public BoundingBox Clone()
        {
            return new BoundingBox(this.min, this.max);
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return String.Format("BoundingBox({0},{1} {2},{3})", this.Left, this.Bottom, this.Right, this.Top);
        }

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns>
        /// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        public override bool Equals(object obj)
        {
            BoundingBox box = obj as BoundingBox;

            if (obj == null)
            {
                return false;
            }
            else
            {
                return (this.min == box.min) && (this.max == box.max);
            }
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode()
        {
            return this.min.GetHashCode() ^ this.max.GetHashCode();
        }
    }
}
